import io
import json
import os
import sys
import time

import chardet
from minio.commonconfig import Tags, CopySource, REPLACE
from tornado import gen

from config import cfg
import jwt
from minio.deleteobjects import DeleteObject
from tornado.web import RequestHandler, Application
from minio import Minio
from utils import auth
from utils.sessions import SessionManger
from models import *

os.environ['TZ'] = 'Asia/Shanghai'

class BaseHandler(RequestHandler):
    '''
    def prepare(self):
        c_sessionid = self.get_cookie('sessionid', '')
        sessionObj = SessionManger.getSessionObjBySid(c_sessionid)
        # 是否重置sessionid(初次是空，要重置)
        if sessionObj.sessionid != c_sessionid:
            self.set_cookie('sessionid', sessionObj.sessionid, expires_days=14)
        self.session = sessionObj
    '''
    def getHeaderData(self):
        try:
            #auth_token = self.request.headers.get("Authorization")
            auth_token = self.request.headers.get("X-Access-Token")
            if not auth_token:
                # 权限拒绝，返回401
                self.set_status(401)  # 给请求者提示 权限拒绝
                self.write({"error": "permission denied！"})
                return
            # 传过来的是字符串 我们需要转换成字节，才能解密
            auth_token = bytes(auth_token, encoding="utf8")  # 字符转字节
            # 对jwt解密
            auth_token = auth.decode_auth_token(auth_token, 'admin', 'Wy666513%')
        # 权限过期会发生异常，我们捕获它
        except jwt.exceptions.ExpiredSignatureError as e:
            # 权限过期了
            self.set_status(403)  # 给请求者提示 资源不可用
            self.write({"error": "permission overdue！"})
            return
        print(auth_token)
        return auth_token.get("data")

    '''
    def on_finish(self) -> None:
        SessionManger.cache2redis(self.session)
    '''


class LoginHandler(BaseHandler):
    def get(self):
        self.render('login.html')

    def post(self):
        uname = self.get_argument("uname", '')
        pwd = self.get_argument("pwd", '')

        # 判断是否登录成功
        if uname == 'zhangshan' and pwd == '123':
            user = User(uname, pwd)
            self.session.set('user', user)
            self.redirect('/center/')
        else:
            self.write(u"(%s,%s)用户或密码错误！" % (uname, pwd))


class CenterHandler(BaseHandler):
    def get(self, *args, **kwargs):
        user = self.session.get('user')
        if user:
            self.write(u'欢迎%s登陆成功！' % (user.uname))
        else:
            self.write("user undefined")


class ApiHandler(BaseHandler):
    def post(self, *args, **kwargs):
        self.write('test')
        auth_token = self.getHeaderData()
        # print(f"auth_token: {auth_token}")
        # if not auth_token:
        #     return
        # # 如果权限通过 那么 我们从 jwt中提取用户id,不接收传来的id防止传入非法id
        # user_id = auth_token.get('id')
        # self.write(user_id)


# oss workspace
class OssBaseHandler(RequestHandler):
    minio_url = cfg['minio']['minio_url']
    access_key = cfg['minio']['access_key']
    secret_key = cfg['minio']['secret_key']
    # 配置请求头，允许跨域，否者在浏览器调用的时候报错误，同时还得加上允许Authorization字段过来
    def set_default_headers(self):
        self.set_header("Access-Control-Allow-Credentials", True)
        self.set_header("Access-Control-Allow-Origin", "*")
        self.set_header("Access-Control-Allow-Headers", '*')
        self.set_header("Access-Control-Allow-Methods", "*")

    def minioInit(self, bucketName):
        client = Minio(self.minio_url, access_key=self.access_key, secret_key=self.secret_key, secure=False)
        if not client.bucket_exists(bucketName):
            client.make_bucket(bucketName)
            # read-write bucket policy
            policy = {
                "Version": "2012-10-17",
                "Statement": [
                    {
                        "Effect": "Allow",
                        "Principal": {"AWS": "*"},
                        "Action": [
                            "s3:GetBucketLocation",
                            "s3:ListBucket",
                            "s3:ListBucketMultipartUploads",
                        ],
                        "Resource": f"arn:aws:s3:::{bucketName}",
                    },
                    {
                        "Effect": "Allow",
                        "Principal": {"AWS": "*"},
                        "Action": [
                            "s3:GetObject",
                            "s3:PutObject",
                            "s3:DeleteObject",
                            "s3:ListMultipartUploadParts",
                            "s3:AbortMultipartUpload",
                        ],
                        "Resource": f"arn:aws:s3:::{bucketName}/*",
                    },
                ],
            }
            client.set_bucket_policy(bucketName, json.dumps(policy))
            # print(c.get_bucket_policy('contract'))
            print(f"{bucketName} created!")
        else:
            print(f"{bucketName} already exists!  ")
        return client

    def getObject(self, filepath, isDownload=True):
        try:
            # Read data from response
            response = self.client.get_object(self.bucketName, filepath)
            # print(dir(response))
            if isDownload:
                self.set_header('Content-Type', 'application/octet-stream')
                self.set_header('Content-Disposition', 'attachment; filename=uikit-3.15.14.tar')
            else:
                stat = self.client.stat_object(self.bucketName, filepath)
                self.set_header("Content-Type", stat.content_type)
            self.write(response.data)
        finally:
            response.close()
            response.release_conn()

    def listObjects(self):
        print(self.bucketName, self.dirname)
        objects = self.client.list_objects(
            self.bucketName, prefix=self.dirname, recursive=True,
        )

        return [{"id_client": self.client.stat_object(self.bucketName, obj.object_name).metadata.get('X-Amz-Meta-Id_client', ''),
                 "memo": eval(self.client.stat_object(self.bucketName, obj.object_name).metadata.get('X-Amz-Meta-Memo', b'')).decode("utf-8"),
                 "filePath": obj.object_name, "version_id": obj.version_id, "bucketName": self.bucketName,
                 "create_time": time.mktime(obj.last_modified.timetuple()), "size": obj.size} for obj in objects]

    def putFile(self, filepath):
        # 文件大小限制
        stat = self.client.stat_object(self.bucketName, filepath)
        if stat.size > cfg['minio']['file_limit_size']:
            self.result.success = False
            self.result.code = 403
            self.result.message = "文件大小不能250M"
            return
        result = self.client.fput_object(
            self.bucketName, filepath.split("/")[-1], filepath,
        )
        print(
            "created {0} object; etag: {1}, version-id: {2}".format(
                result.object_name, result.etag, result.version_id,
            ),
        )

    def delFiles(self, filePaths):
        if isinstance(filePaths, list):
            delete_object_list = map(
                lambda x: DeleteObject(x),
                filePaths
            )
            hasError = False
            errors = self.client.remove_objects(self.bucketName, delete_object_list)
            for error in errors:
                print("error occured when deleting object", error)
                hasError = True
            if hasError:
                self.result['message'] ="delete failed"
                self.result['code'] = 500
                self.result['success'] = False
        else:
            self.client.remove_object(self.bucketName, filePaths)

    def updateMemo(self, object_name, metadata=None):
        try:
            tmp_object_name = object_name + "_update_metadata_tmp"
            tmp_obj_res = self.client.copy_object(
                self.bucketName, tmp_object_name, CopySource(self.bucketName, object_name))
            print("copy to tmp", self.bucketName,
                  object_name + "->" + tmp_object_name, tmp_obj_res)
            print("tmp to origin")
            #self.client.remove_object(self.bucketName, object_name)
            org_obj_res = self.client.copy_object(
                self.bucketName, object_name,
                CopySource(self.bucketName, tmp_object_name),
                metadata=metadata, metadata_directive=REPLACE)
            print(org_obj_res)
            self.client.remove_object(self.bucketName, tmp_object_name)
            print("remove tmp-obj")
        except Exception as e:
            print(e)
            self.result['code'] = 500
            self.result['success'] = False
            self.result['message'] = f'error: {e}'
        finally:
            self.feedback()

    def initialize(self):
        self.result = {'code': 200, 'success': True, 'message': 'OK', 'result': []}
        self.dirname = self.get_argument('bizPath', '')
        self.bucketName = self.get_argument("bucketName", 'contract')
        self.client = self.minioInit(self.bucketName);

    def options(self):
        self.write('ok')

    def feedback(self):
        self.set_header('Content-Type', 'application/json; charset=UTF-8')
        self.finish(self.result)

class ossReadHandler(OssBaseHandler):
    def get(self):
        filepath = self.get_argument('filePath', '') or self.get_argument('fileName', '')
        print(filepath)
        self.getObject(filepath, isDownload=False)


class ossTagHandler(OssBaseHandler):
    def put(self):
        try:
            memo = self.get_argument('memo')
            filepath = self.get_argument('filePath')
            print(memo)
            if memo:
                tags = Tags.new_object_tags()
                tags["memo"] = memo
                tags["User"] = "jsmith"
                self.client.set_object_tags(self.bucketName, filepath, tags)
        except Exception as e:
            self.result.code = 500
            self.result.success = False
            print(e)
        self.feedback()



class ossDownloadHandler(OssBaseHandler):
    def get(self):
        filepath = self.get_argument('filePath')
        self.getObject(filepath)

class testHandler(OssBaseHandler):
    def sleep2(self):
        print("--3")
        yield
        time.sleep(30)
        print("--4")

    async def get(self):
        self.sleep2()
        # yield gen.sleep(10)
        print("sleepped")
        result = self.client.stat_object(self.bucketName, "希凯易_receipt_06-05-2021.pdf")
        for k in result.__dict__:
            print(k, getattr(result, k))
        self.feedback()

class ossHandler(OssBaseHandler):
    def get(self):
        self.result['result'] = self.listObjects()
        self.feedback()

    def post(self):
        id_client = self.get_argument("id_client")
        memo = self.get_argument("memo")
        file = self.request.files.get('file', '')
        metadata = {"memo": memo.encode(), "id_client": id_client}
        if file:
            filename, data, content_type = file[0].values()
        else:
            filePath = self.get_argument('filePath')
            print(filePath, self.bucketName)
            self.updateMemo(filePath, metadata)
            return
        # 日志
        encoding = chardet.detect(b"memo")
        print(encoding)
        print(sys.getsizeof(data))

        # print(self.bucketName, f'{dirname}{filename}', data, len(data))
        if sys.getsizeof(data) > eval(cfg['minio']['file_limit_size']):
            self.result.success = False
            self.result.code = 403
            self.result.message = "文件大小不能100M"
            self.feedback()
            return
        try:
            self.client.put_object(
                self.bucketName, f'{self.dirname}{filename}', io.BytesIO(data), len(data),
                content_type=content_type, metadata=metadata,
            )

        except Exception as e:
            self.result.code = 500
            self.result.success = False
            print(e)
        self.feedback()

    def delete(self):
        filePaths = self.get_argument('filePaths')
        try:
            self.delFiles(filePaths)
        except Exception as e:
            print(e)
            self.result.code = 500
            self.result.success = False
        self.result['message'] = 'deleted'
        self.feedback()

    def put(self):
        data = json.loads(self.request.body)
        self.bucketName = data.get('bucketName', 'contract')
        if data['filePaths']:
            try:
                self.delFiles(data['filePaths'])
            except Exception as e:
                print(e)
                self.result.code = 500
                self.result.success = False
            self.result['message'] = 'all deleted'
        self.feedback()

